#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <sys/select.h>
#include <string.h>
#include <poll.h>
#include <time.h>

// 监听套接字描述符
int sockfd = -1;



// 定义一个套接字描述符数组
#define MAXSIZE 100
struct pollfd fds[MAXSIZE];

#define MAX_ROOMS 100
#define MAX_MESSAGES_PER_ROOM 100 // 每个房间最多保存100条消息
#define MAX_MESSAGE_LENGTH 256 // 每条消息最大长度

int fd_to_room[MAXSIZE]; // fd索引->房间号
int room_online_count[MAX_ROOMS]; // 每个房间的在线人数
char fd_to_ip[MAXSIZE][INET_ADDRSTRLEN]; // fd索引->IP地址
int fd_to_port[MAXSIZE]; // fd索引->端口号

// 聊天记录结构
typedef struct {
    char message[MAX_MESSAGE_LENGTH];
    char timestamp[32];
    char sender_ip[INET_ADDRSTRLEN];
    int sender_port;
} ChatMessage;

// 每个房间的聊天记录
ChatMessage room_messages[MAX_ROOMS][MAX_MESSAGES_PER_ROOM];
int room_message_count[MAX_ROOMS]; // 每个房间当前消息数量
int room_message_index[MAX_ROOMS]; // 每个房间当前写入位置


// 保存聊天记录到指定房间
void save_chat_message(int room, const char* message, const char* sender_ip, int sender_port)
{
    if (room < 0 || room >= MAX_ROOMS) 
        return;
    
    // 获取当前时间
    time_t now = time(NULL);
    struct tm *tm_info = localtime(&now);
    char timestamp[32];
    strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info);
    
    // 保存消息到循环数组
    int index = room_message_index[room];
    strncpy(room_messages[room][index].message, message, MAX_MESSAGE_LENGTH - 1);
    room_messages[room][index].message[MAX_MESSAGE_LENGTH - 1] = '\0';
    strncpy(room_messages[room][index].timestamp, timestamp, sizeof(room_messages[room][index].timestamp) - 1);
    strncpy(room_messages[room][index].sender_ip, sender_ip, INET_ADDRSTRLEN - 1);
    room_messages[room][index].sender_port = sender_port;
    
    // 更新索引和计数
    room_message_index[room] = (room_message_index[room] + 1) % MAX_MESSAGES_PER_ROOM;
    if (room_message_count[room] < MAX_MESSAGES_PER_ROOM) 
    {
        room_message_count[room]++;
    }
    
}
// 连接服务器
void init_server()
{
    // 创建通信节点，创建流式套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
    {
        perror("socket error");
        exit(1);
    }
    // 设置套接字选项，以允许地址重用
    int opval = 1; // 1表示开启地址重用功能
    int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opval, sizeof(int));
    if (ret == -1)
    {
        perror("set sockopt error");
        exit(1);
    }

    // 获取套接字选项的值
    int value = -1;
    int len = sizeof(int);
    ret = getsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &value, &len);
    if (ret == -1)
    {
        perror("getsocketopt error");
        exit(1);
    }
    printf("value = %d \n", value);
    
    // 绑定通信地址
    struct  sockaddr_in addr;
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(10010);
    addr.sin_addr.s_addr = INADDR_ANY;
    // 绑定
    int r = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if (r == -1)
    {
        perror("bind error");
        exit(1);
    }

    // 监听，监听完成后，sockfd称为监听套接字，只能用来接收连接，不能用来通信
    r = listen(sockfd, 10);
    if (r ==-1)
    {
        perror("listen error");
        exit(1);
    }
    printf( "sever init success! \n");
}

// 处理客户端信息
void handle_read_event()
{
    for (int i = 0; i < MAXSIZE; i++)
    {
        if (fds[i].revents == POLLIN && fds[i].fd == sockfd)
        {
            // 监听套接字描述符，有数据则用accept函数接收
            struct sockaddr_in client_addr;
            socklen_t client_len = sizeof(client_addr);
            int confd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);
            if (confd == -1)
            {
                perror("accept error");
                continue;
            }

            // 获取客户端IP地址和端口
            char client_ip[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
            int client_port = ntohs(client_addr.sin_port);
            
            printf("新客户端连接: IP=%s, 端口=%d, 套接字=%d\n", client_ip, client_port, confd);

            // 先读取房间号
            char room_buf[32] = {0};
            int room_r = read(confd, room_buf, sizeof(room_buf)-1);
            if (room_r <= 0) 
            {
                printf("未收到房间号，关闭连接\n");
                close(confd);
                continue;
            }
            int room = atoi(room_buf);
            // 新增：房间号合法性判断
            if (room < 0 || room >= MAX_ROOMS) {
                printf("房间号非法(只能为0~99)，关闭连接\n");
                close(confd);
                continue;
            }
            printf("客户端套接字%d加入房间%d\n", confd, room);

            // 把接收到的套接字加入到文件结构数组
            int found = 0;
            for (int j = 0; j < MAXSIZE; j++)
            {
                if (fds[j].fd == -1)
                {
                    fds[j].fd = confd;
                    fds[j].events = POLLIN;
                    fd_to_room[j] = room;
                    strcpy(fd_to_ip[j], client_ip); // 保存IP地址
                    fd_to_port[j] = client_port; // 保存端口号
                    found = 1;
                    break;
                }
            }
            if (!found) 
            {
                printf("连接数已满，拒绝新连接\n");
                close(confd);
            } 
            else 
            {
                // 更新房间在线人数
                room_online_count[room]++;
                
                // 广播上线通知到同房间
                char online_msg[256];
                snprintf(online_msg, sizeof(online_msg), "[%s:%d] 加入了房间%d (当前房间在线人数: %d)\n", client_ip, client_port, room, room_online_count[room]);
                for (int n = 0; n < MAXSIZE; n++) 
                {
                    if (fds[n].fd > 0 && fds[n].fd != sockfd && fds[n].fd != confd && fd_to_room[n] == room) 
                    {
                        int w = write(fds[n].fd, online_msg, strlen(online_msg));
                        if (w == -1) 
                        {
                            perror("write error");
                            close(fds[n].fd);
                            fds[n].fd = -1;
                            room_online_count[room]--; // 减少在线人数
                        }
                    }
                }
            
                // 向新用户发送当前房间在线人数
                char welcome_msg[256];
                snprintf(welcome_msg, sizeof(welcome_msg), "欢迎加入房间%d!当前房间在线人数: %d\n", room, room_online_count[room]);
                write(confd, welcome_msg, strlen(welcome_msg));
            }

        }
        else if (fds[i].revents == POLLIN)
        {
            // 读取数据
            char buf[MAXSIZE] = {0};
            int r = read(fds[i].fd, buf, sizeof(buf));
            // 打印消息发送者的IP和端口
            int room = fd_to_room[i];
            struct sockaddr_in sender_addr;
            socklen_t sender_len = sizeof(sender_addr);
            if (getpeername(fds[i].fd, (struct sockaddr*)&sender_addr, &sender_len) == 0) 
            {
                char sender_ip[INET_ADDRSTRLEN];
                inet_ntop(AF_INET, &sender_addr.sin_addr, sender_ip, INET_ADDRSTRLEN);
                int sender_port = ntohs(sender_addr.sin_port);
                printf("%d:[%s:%d]:%s \n", room, sender_ip, sender_port, buf);
            }
            if (r == -1)
            {
                perror("read error");
                continue;

            }
            else if (r == 0) // 断开连接，把连接套接字从集合中移除
            {
                // 获取断开连接的客户端地址
                struct sockaddr_in disconnect_addr;
                socklen_t disconnect_len = sizeof(disconnect_addr);
                char disconnect_ip[INET_ADDRSTRLEN] = "unknown";
                int disconnect_port = 0;
                if (getpeername(fds[i].fd, (struct sockaddr*)&disconnect_addr, &disconnect_len) == 0)
                {
                    inet_ntop(AF_INET, &disconnect_addr.sin_addr, disconnect_ip, INET_ADDRSTRLEN);
                    disconnect_port = ntohs(disconnect_addr.sin_port);
                    printf("客户端断开连接: IP=%s, 端口=%d, 套接字=%d\n", disconnect_ip, disconnect_port, fds[i].fd);
                }
                else
                {
                    printf("客户端断开连接: 套接字=%d\n", fds[i].fd);
                }
                // 更新房间在线人数
                room_online_count[room]--;
                
                // 广播下线通知，只通知同一房间的其他用户
                int room = fd_to_room[i];
                char offline_msg[256];
                snprintf(offline_msg, sizeof(offline_msg), "[%s:%d] 离开了房间%d (当前房间在线人数: %d)\n", 
                        disconnect_ip, disconnect_port, room, room_online_count[room]);
                for (int n = 0; n < MAXSIZE; n++)
                {
                    if (fds[n].fd > 0 && n != i && fds[n].fd != sockfd && fd_to_room[n] == room)
                    {
                        int w = write(fds[n].fd, offline_msg, strlen(offline_msg));
                        if (w == -1)
                        {
                            perror("write error");
                            close(fds[n].fd);
                            fds[n].fd = -1;
                            fd_to_room[n] = 0;
                            room_online_count[room]--; // 减少在线人数
                        }
                    }
                }
                close(fds[i].fd);
                fds[i].fd = -1;
                fd_to_room[i] = 0;
                memset(fd_to_ip[i], 0, INET_ADDRSTRLEN); // 清空IP地址
                fd_to_port[i] = 0; // 清空端口号
                continue;
            }
            else
            {
                // 获取发送消息的客户端房间号
                int room = fd_to_room[i];
                
                // 检查是否是特殊命令
                if (strncmp(buf, "/online", 7) == 0)
                {
                    // 查询在线人数命令
                    char online_info[256];
                    snprintf(online_info, sizeof(online_info), "房间%d当前在线人数: %d\n", room, room_online_count[room]);
                    write(fds[i].fd, online_info, strlen(online_info));
                }
                else if (strncmp(buf, "/rooms", 6) == 0)
                {
                    // 查询所有房间在线人数
                    char rooms_info[1024] = "所有房间在线人数:\n";
                    for (int r = 0; r < MAX_ROOMS; r++)
                    {
                        if (room_online_count[r] > 0)
                        {
                            char room_info[64];
                            snprintf(room_info, sizeof(room_info), "房间%d: %d人\n", r, room_online_count[r]);
                            strcat(rooms_info, room_info);
                        }
                    }
                    write(fds[i].fd, rooms_info, strlen(rooms_info));
                }
                else if (strncmp(buf, "/users", 6) == 0)
                {
                    // 查询当前房间所有在线用户IP
                    char users_info[2048] = "";
                    snprintf(users_info, sizeof(users_info), "房间%d在线用户列表:\n", room);
                    int user_count = 0;
                    
                    for (int n = 0; n < MAXSIZE; n++)
                    {
                        if (fds[n].fd > 0 && fds[n].fd != sockfd && fd_to_room[n] == room)
                        {
                            char user_info[128];
                            snprintf(user_info, sizeof(user_info), "%d. [%s:%d]\n", 
                                    ++user_count, fd_to_ip[n], fd_to_port[n]);
                            strcat(users_info, user_info);
                        }
                    }
                    
                    if (user_count == 0)
                    {
                        strcat(users_info, "当前房间无其他用户\n");
                    }
                    else
                    {
                        char count_info[64];
                        snprintf(count_info, sizeof(count_info), "\n总计: %d人\n", user_count);
                        strcat(users_info, count_info);
                    }
                    
                    write(fds[i].fd, users_info, strlen(users_info));
                }
                else if (strncmp(buf, "/history", 8) == 0)
                {
                    // 查询聊天记录
                    char history_info[8192] = "";
                    snprintf(history_info, sizeof(history_info), "房间%d聊天记录 (最近%d条):\n", room, room_message_count[room]);
                    
                    if (room_message_count[room] == 0)
                    {
                        strcat(history_info, "暂无聊天记录\n");
                    }
                    else
                    {
                        // 计算起始位置
                        int start_index;
                        if (room_message_count[room] < MAX_MESSAGES_PER_ROOM) {
                            start_index = 0;
                        } else {
                            start_index = room_message_index[room];
                        }
                        
                        // 显示聊天记录
                        for (int j = 0; j < room_message_count[room]; j++)
                        {
                            int msg_index = (start_index + j) % MAX_MESSAGES_PER_ROOM;
                            char msg_info[512];
                            snprintf(msg_info, sizeof(msg_info), "[%s] [%s:%d]: %s\n", 
                                    room_messages[room][msg_index].timestamp,
                                    room_messages[room][msg_index].sender_ip,
                                    room_messages[room][msg_index].sender_port,
                                    room_messages[room][msg_index].message);
                            strcat(history_info, msg_info);
                        }
                    }
                    
                    write(fds[i].fd, history_info, strlen(history_info));
                }
                else
                {
                    // 获取发送消息的客户端地址
                    struct sockaddr_in sender_addr;
                    socklen_t sender_len = sizeof(sender_addr);
                    if (getpeername(fds[i].fd, (struct sockaddr*)&sender_addr, &sender_len) == 0)
                    {
                        char sender_ip[INET_ADDRSTRLEN];
                        inet_ntop(AF_INET, &sender_addr.sin_addr, sender_ip, INET_ADDRSTRLEN);
                        int sender_port = ntohs(sender_addr.sin_port);
                        
                        // 保存聊天记录
                        save_chat_message(room, buf, sender_ip, sender_port);
                        
                        // 构造带地址信息的消息
                        char full_msg[1024];
                        snprintf(full_msg, sizeof(full_msg), "[%s:%d] %s", sender_ip, sender_port, buf);
                        
                        // 只广播给同房间的其他客户端
                        for (int n = 0; n < MAXSIZE; n++)
                        {
                            if (fds[n].fd > 0 && fds[n].fd != sockfd && n != i && fd_to_room[n] == room)
                            {
                                int w = write(fds[n].fd, full_msg, strlen(full_msg));
                                if (w == -1)
                                {
                                    perror("write error");
                                    close(fds[n].fd);
                                    fds[n].fd = -1;
                                    fd_to_room[n] = 0;
                                    room_online_count[room]--; // 减少在线人数
                                }
                            }
                        }
                    }
                    else
                    {
                        // 如果获取地址失败，直接广播原消息到同房间
                        for (int n = 0; n < MAXSIZE; n++)
                        {
                            if (fds[n].fd > 0 && fds[n].fd != sockfd && n != i && fd_to_room[n] == room)
                            {
                                int w = write(fds[n].fd, buf, r);
                                if (w == -1)
                                {
                                    perror("write error");
                                    close(fds[n].fd);
                                    fds[n].fd = -1;
                                    fd_to_room[n] = 0;
                                    room_online_count[room]--; // 减少在线人数
                                }
                            }
                        }
                    }
                }
            }
           
        }
    }
    
}


int main(int argc, char const *argv[])
{
    // 服务器初始化
    init_server();

    // 初始化数组
    for (int i = 0; i < MAXSIZE; i++)
    {
        fds[i].fd = -1;
        fd_to_room[i] = 0; // 初始化为0，表示未分配房间
    }
    
    // 初始化房间在线人数
    for (int i = 0; i < MAX_ROOMS; i++)
    {
        room_online_count[i] = 0;
    }
    
    // 初始化聊天记录数组
    for (int i = 0; i < MAX_ROOMS; i++)
    {
        room_message_count[i] = 0;
        room_message_index[i] = 0;
        for (int j = 0; j < MAX_MESSAGES_PER_ROOM; j++)
        {
            memset(&room_messages[i][j], 0, sizeof(ChatMessage));
        }
    }

    fds[0].fd = sockfd;
    fds[0].events = POLLIN;

    while (1)
    {
        int ret = poll(fds, MAXSIZE, 3000);
        if (ret == -1)
        {
            perror("poll error");
            continue;

        }
        else if (ret == 0)
        {
            continue;

        }
        else
        {
            handle_read_event();
        }
    }
    return 0;
}

